package com.xinmao.quantitative.utils;

import lombok.extern.slf4j.Slf4j;
import org.ta4j.core.*;
import org.ta4j.core.analysis.cost.CostModel;
import org.ta4j.core.criteria.*;
import org.ta4j.core.criteria.pnl.*;
import org.ta4j.core.num.DecimalNum;
import org.ta4j.core.num.Num;
import org.ta4j.core.reports.PerformanceReport;
import org.ta4j.core.reports.PositionStatsReport;
import org.ta4j.core.reports.TradingStatement;

import java.time.Duration;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.StringJoiner;
import java.util.concurrent.atomic.AtomicReference;

/**
 * @Title: ReportUtil
 * @Author: XYe13
 * @Date: 5/6/2024 14:08
 * @Description: <TODO description class purpose>
 */

@Slf4j
public class ReportUtil {

    public static StringBuilder printStatementReport(TradingStatement statement) {
        StringBuilder resultBuilder = new StringBuilder();
        resultBuilder.append(System.lineSeparator());
        resultBuilder.append("######### ")
                .append(statement.getStrategy().getName())
                .append(" #########")
                .append(System.lineSeparator())
                .append(printPerformanceReport(statement.getPerformanceReport()))
                .append(",")
                .append(printPositionStats(statement.getPositionStatsReport()));
        return resultBuilder;
    }

    public static StringBuilder printPerformanceReport(PerformanceReport report) {
        StringBuilder resultBuilder = new StringBuilder();
        resultBuilder.append("浮亏:")
                .append(report.getTotalLoss())
                .append(",浮盈:")
                .append(report.getTotalProfit())
                .append(",净利:")
                .append(report.getTotalProfitLoss())
                .append(",盈亏比:")
                .append(report.getTotalProfitLossPercentage());
        return resultBuilder;
    }

    public static StringBuilder printPositionStats(PositionStatsReport report) {
        StringBuilder resultBuilder = new StringBuilder();
        resultBuilder.append("亏损:")
                .append(report.getLossCount())
                .append(",盈利:")
                .append(report.getProfitCount())
                .append(",持平:")
                .append(report.getBreakEvenCount());
        return resultBuilder;
    }

    public static String printReport(List<TradingStatement> tradingStatements) {
        StringJoiner resultJoiner = new StringJoiner(System.lineSeparator());
        for (TradingStatement statement : tradingStatements) {
            resultJoiner.add(ReportUtil.printStatementReport(statement).toString());
        }
        return resultJoiner.toString();
    }

    public static String printReportTotalResult(List<TradingStatement> tradingStatements) {

        AtomicReference<Num> profitCount = new AtomicReference<>(DecimalNum.valueOf(0));
        AtomicReference<Num> lossCount = new AtomicReference<>(DecimalNum.valueOf(0));
        AtomicReference<Num> breakEvenCount = new AtomicReference<>(DecimalNum.valueOf(0));

        AtomicReference<Num> totalLoss = new AtomicReference<>(DecimalNum.valueOf(0));
        AtomicReference<Num> totalProfit = new AtomicReference<>(DecimalNum.valueOf(0));
//        AtomicReference<Num> totalProfitLossPercentage = new AtomicReference<>(DecimalNum.valueOf(0));

        tradingStatements.forEach(tradingStatement -> {
            profitCount.set(profitCount.get().plus(tradingStatement.getPositionStatsReport().getProfitCount()));
            lossCount.set(lossCount.get().plus(tradingStatement.getPositionStatsReport().getLossCount()));
            breakEvenCount.set(breakEvenCount.get().plus(tradingStatement.getPositionStatsReport().getBreakEvenCount()));

            totalLoss.set(totalLoss.get().plus(tradingStatement.getPerformanceReport().getTotalLoss()));
            totalProfit.set(totalProfit.get().plus(tradingStatement.getPerformanceReport().getTotalProfit()));
//            totalProfitLossPercentage.set(totalProfitLossPercentage.get().plus(tradingStatement.getPerformanceReport().getTotalProfitLossPercentage()));
        });
        return System.lineSeparator() +
                "######### Total Result #########" +
                System.lineSeparator() +
                "总盈利:" + profitCount +
                ",总亏损:" + lossCount +
                ",总持平:" + breakEvenCount +
                ",总浮亏:" + totalLoss +
                ",总浮盈:" + totalProfit ;
//                ",总浮盈比:" + totalProfitLossPercentage.get().dividedBy(DecimalNum.valueOf(tradingStatements.size()));
    }

    public static void printReportTotalResult(TradingRecord tradingRecord, BarSeries series){
        printTradeList(tradingRecord, series);
        log.info("######### strategy Result ############");
        log.info("盈利：{}",new ProfitCriterion().calculate(series, tradingRecord));
        log.info("亏损：{}",new LossCriterion().calculate(series, tradingRecord).toString());
        log.info("盈亏比：{}",new ReturnCriterion().calculate(series, tradingRecord).toString());
        log.info("年化收益率%：{}",getAnnualizedReturnReport(tradingRecord, series).toString());
        log.info("单次收益率%：{}",new ProfitLossPercentageCriterion().calculate(series, tradingRecord).toString());
        log.info("最大回撤%：{}",new ReturnOverMaxDrawdownCriterion().calculate(series, tradingRecord).multipliedBy(series.hundred()));
        log.info("盈利仓位百分比：{}",new PositionsRatioCriterion(AnalysisCriterion.PositionFilter.PROFIT).calculate(series, tradingRecord).toString());
        log.info("亏损仓位百分比：{}",new PositionsRatioCriterion(AnalysisCriterion.PositionFilter.LOSS).calculate(series, tradingRecord).toString());
        log.info("回报率（开始持有到结束卖出）：{}",new VersusEnterAndHoldCriterion(new ReturnCriterion()).calculate(series, tradingRecord).toString());
        //置信水平值95%
//        log.info("风险率：{}",new ValueAtRiskCriterion(0.95).calculate(series, tradingRecord).toString());
        log.info("(SQN)质量指数：【差，可交易：1.6–1.9，一般：2.0–2.4：普通，2.5–2.9：良好，3.0–5.0：杰出，5.0–6.9：一流，大于7:出色】");
        log.info("(SQN)质量指数：{}",new SqnCriterion(new ReturnCriterion()).calculate(series, tradingRecord).toString());
        //log.info("毛利成本损失率：{}",new ProfitLossRatioCriterion().calculate(series, tradingRecord).toString());
        //log.info("期望值：{}",new ExpectancyCriterion().calculate(series, tradingRecord).toString());
        //log.info("盈利次数：{}",new NumberOfWinningPositionsCriterion().calculate(series, tradingRecord).toString());

        // 进入并持有
        //log.info("利润：{}",new VersusEnterAndHoldCriterion(new ReturnCriterion()).calculate(series, tradingRecord).toString());
        // log.info("预期缺口风险价值：{}",new ExpectedShortfallCriterion(0.95).calculate(series, tradingRecord).toString());

    }

    public static void printTradeList(TradingRecord tradingRecord,BarSeries series) {
        tradingRecord.getPositions().forEach(position -> {
            String entryDate = series.getBar(position.getEntry().getIndex()).getEndTime().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
            log.info("{},{},{}",series.getName(),entryDate,position.getEntry().toString());
            String exitDate = series.getBar(position.getExit().getIndex()).getEndTime().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
            log.info("{},{},{}",series.getName(),exitDate,position.getExit().toString());
        });
        printCurrentPosition(tradingRecord, series);
    }

    public static void printCurrentPosition(TradingRecord tradingRecord, BarSeries series) {
        if(tradingRecord.getCurrentPosition().getEntry() != null){
            String entryDate = series.getBar(tradingRecord.getCurrentPosition().getEntry().getIndex()).getEndTime().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
            log.info("{},{},{}",series.getName(),entryDate,tradingRecord.getCurrentPosition().getEntry().toString());
        }
        if(tradingRecord.getCurrentPosition().getExit() != null){
            String exitDate = series.getBar(tradingRecord.getCurrentPosition().getExit().getIndex()).getEndTime().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
            log.info("{},{},{}",series.getName(),exitDate,tradingRecord.getCurrentPosition().getExit().toString());
        }
    }

    /**
     *
     * */
    public static Num getAnnualizedReturnReport(TradingRecord tradingRecord, BarSeries series) {
        Num annualizedFactor = DecimalNum.valueOf(365).dividedBy(DecimalNum.valueOf(Duration.between(series.getFirstBar().getEndTime(),
                series.getLastBar().getEndTime()).toDays()));
        return new ProfitLossPercentageCriterion().calculate(series, tradingRecord).multipliedBy(annualizedFactor);
    }

}
